/******************************************************************************
	Cobalt - utility libraries for building XNA games
	Copyright (C) 2008 Philip Djonov

	This program is free software; you can redistribute it and/or modify it
	under the terms of the GNU General Public License as published by the Free
	Software Foundation; either version 2 of the License, or (at your option)
	any later version.

	This program is distributed in the hope that it will be useful, but
	WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
	or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
	for more details.

	You should have received a copy of the GNU General Public License along
	with this program; if not, write to the
	
		Free Software Foundation, Inc.
		51 Franklin Street, Fifth Floor
		Boston, MA  02110-1301, USA.
******************************************************************************/

using System;

using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;

namespace Cobalt.Framework.Graphics
{

	public sealed class StaticGeometrySurface : GeometrySurface
	{
		internal StaticGeometrySurface( StaticGeometryBuffer owner )
			: base( owner )
		{
		}

		internal Material Material;

		internal int FirstVertex;
		internal int VertexCount;
		internal int FirstIndex;
		internal int PrimitiveCount;

		internal float BoundingRadius;
	}

	public sealed class StaticGeometryBuffer : GeometrySource
	{
		internal StaticGeometryBuffer() { }

		internal VertexBuffer VertexBuffer;
		internal VertexDeclaration VertexDeclaration;
		internal int VertexStride;
		internal IndexBuffer IndexBuffer;

		internal void Dispose()
		{
			Helpers.DisposeAndNull( ref VertexBuffer );
			Helpers.DisposeAndNull( ref VertexDeclaration );
			Helpers.DisposeAndNull( ref IndexBuffer );
		}

		protected internal override void Begin( ShadingContext context, IPrerenderHandler prerenderHandler )
		{
			GraphicsDevice device = context.GraphicsDevice;

			device.VertexDeclaration = VertexDeclaration;
			device.Vertices[0].SetSource( VertexBuffer, 0, VertexStride );
			device.Indices = IndexBuffer;
		}

		protected internal override void DrawSurfaces( DrawRecord[] surfaces, int index, int count, ShadingContext context, IPrerenderHandler prerenderHandler )
		{
			GraphicsDevice device = context.GraphicsDevice;
			PerformanceCounters pc = context.PerformanceCounters;

			int lastSurf = index + count;
			for( int e, s = index; s < lastSurf; s = e )
			{
				StaticGeometrySurface ss = surfaces[s].surface as StaticGeometrySurface;
				
				int primCount = ss.PrimitiveCount;
				int vertCount = ss.VertexCount;

				for( e = s + 1; e < lastSurf; e++ )
				{
					StaticGeometrySurface se = surfaces[e].surface as StaticGeometrySurface;
					if( ss.FirstVertex + vertCount != se.FirstVertex )
						break;

					vertCount += se.VertexCount;
					primCount += se.PrimitiveCount;
				}

				device.DrawIndexedPrimitives( PrimitiveType.TriangleList,
					0, ss.FirstVertex, vertCount, ss.FirstIndex, primCount );
				pc.UpdateGeometryCounters( vertCount, primCount, 1 );
			}
		}
	}

	public sealed class StaticGeometry : ISurfaceList, IDisposable
	{
		internal StaticGeometry() { }

		internal StaticGeometryBuffer[] Buffers;
		internal StaticGeometrySurface[] Surfaces;

		public void AppendSurfaces( DrawList surfList, Matrix matrix, ShadingContext context )
		{
			if( surfList == null )
				throw new ArgumentNullException( "surfList" );
			if( context == null )
				throw new ArgumentNullException( "context" );

			if( Buffers == null )
				throw new ObjectDisposedException( "StaticGeometry" );

			int matrixId = surfList.RegisterMatrix( ref matrix );

			foreach( StaticGeometrySurface surf in Surfaces )
				surfList.AddDrawSurface( surf, context.OverrideMaterial ?? surf.Material, matrixId );
		}

		#region ISurfaceList Members

		IPvs ISurfaceList.CreatePvs()
		{
			return null;
		}

		void ISurfaceList.AppendSurfaces( DrawList surfList, IPvs pvs, Matrix matrix, ShadingContext context )
		{
			AppendSurfaces( surfList, matrix, context );
		}

		#endregion

		#region IDisposable Members

		public void Dispose()
		{
			if( Buffers != null )
			{
				foreach( StaticGeometryBuffer buf in Buffers )
					buf.Dispose();
			}

			Buffers = null;
			Surfaces = null;
		}

		#endregion
	}

}